package cn.edu.cug.cs.gtl.oi.inventor.demo;

import cn.edu.cug.cs.gtl.oi.utils.Example;
import cn.edu.cug.cs.gtl.oi.utils.ViewerComponentsFactory;
import com.openinventor.inventor.SbBox3f;
import com.openinventor.inventor.SbColor;
import com.openinventor.inventor.SbDataType;
import com.openinventor.inventor.SbVec3i32;
import com.openinventor.inventor.manips.SoTransformerManip;
import com.openinventor.inventor.nodes.*;
import com.openinventor.inventor.viewercomponents.awt.IViewerExaminer;
import com.openinventor.ldm.nodes.SoTransferFunction;
import com.openinventor.volumeviz.nodes.SoVolumeData;
import com.openinventor.volumeviz.nodes.SoVolumeTriangleStripSet;

import java.awt.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class VolumeGeometry extends Example
{

    // Positions of all of the vertices:
    static final float vertexPositions[][] =
            {
                    { 0.0f,12.0f, 0.0f }, { 0.0f,15.0f, 0.0f},
                    { 2.1f,12.1f,-0.2f }, { 2.1f,14.6f,-0.2f},
                    { 4.0f,12.5f,-0.7f }, { 4.0f,14.5f,-0.7f},
                    { 4.5f,12.6f,-0.8f }, { 4.5f,14.4f,-0.8f},
                    { 5.0f,12.7f,-1.0f }, { 5.0f,14.4f,-1.0f},
                    { 4.5f,12.8f,-1.4f }, { 4.5f,14.6f,-1.4f},
                    { 4.0f,12.9f,-1.6f }, { 4.0f,14.8f,-1.6f},
                    { 3.3f,12.9f,-1.8f }, { 3.3f,14.9f,-1.8f},
                    { 3.0f,13.0f,-2.0f }, { 3.0f,14.9f,-2.0f},
                    { 3.3f,13.1f,-2.2f }, { 3.3f,15.0f,-2.2f},
                    { 4.0f,13.2f,-2.5f }, { 4.0f,15.0f,-2.5f},
                    { 6.0f,13.5f,-2.2f }, { 6.0f,14.8f,-2.2f},
                    { 8.0f,13.4f,-2.0f }, { 8.0f,14.6f,-2.0f},
                    {10.0f,13.7f,-1.8f }, {10.0f,14.4f,-1.8f},
                    {12.0f,14.0f,-1.3f }, {12.0f,14.5f,-1.3f},
                    {15.0f,14.9f,-1.2f }, {15.0f,15.0f,-1.2f},

                    {-0.5f,15.0f, 0.0f }, {-0.5f, 0.0f, 0.0f},   // the flagpole
                    { 0.0f,15.0f, 0.5f }, { 0.0f, 0.0f, 0.5f},
                    { 0.0f,15.0f,-0.5f }, { 0.0f, 0.0f,-0.5f},
                    {-0.5f,15.0f, 0.0f }, {-0.5f, 0.0f, 0.0f}
            };

    // Number of vertices in each strip.
    static final int numVertices[] =
            {
                    32, // flag
                    8   // pole
            };

    private IViewerExaminer myViewer;

    public static void main(String[] args) {
        VolumeGeometry example = new VolumeGeometry();
        example.demoMain("VolumeGeometry");
    }

    @Override
    public void start() {
        myViewer = ViewerComponentsFactory.createViewerExaminer();

        // Volume dimensions
        // Note this is the same (tiny) volume used in the simpleVolume example
        final int dimX = 10;
        final int dimY = 10;
        final int dimZ = 10;

        // Create data :
        // Note: 0 < data[i] < number of entries in the colorMap.
        // If each data value has the same value A the volume will be unicolor
        // with the color described by the Ath entries of the colorMap.
        // This volume data will use every color in the colorMap
        int capacity = dimX * dimY * dimZ;
        ByteBuffer data = ByteBuffer.allocateDirect(capacity);
        data.order(ByteOrder.nativeOrder());
        for (int i = 0; i < capacity; i++) {
            data.put(Integer.valueOf(i % 256).byteValue());
        }

        // Node to hold the volume data
        // In the first call we specify the voxel dimensions and the actual data.
        // In the second call we specify the 3D extent of the volume in modeling coords.
        // Note that the geometric size and position of the volume does not have to be
        // the same as the dimensions of the volume data.
        SoVolumeData pVolData = new SoVolumeData();
        pVolData.data.setValue(new SbVec3i32(dimX, dimY, dimZ), new SbDataType(SbDataType.DataTypes.UNSIGNED_BYTE), data);
        pVolData.extent.setValue(new SbBox3f( -dimX, -dimY, -dimZ, dimX, dimY, dimZ));
        SbBox3f volSize = new SbBox3f( -1, -1, -8, 15, 15, 8);
        pVolData.extent.setValue(volSize);

        // Use a predefined colorMap with the SoTransferFunction
        SoTransferFunction pTransFunc = new SoTransferFunction();
        pTransFunc.predefColorMap.setValue(SoTransferFunction.PredefColorMaps.STANDARD);

        // Define coordinates and base color for volume geometry
        SoVertexProperty myVertexProperty = new SoVertexProperty();
        myVertexProperty.orderedRGBA.set1Value(0, 0xFFFFFFFF);
        myVertexProperty.vertex.setValues(0, vertexPositions);

        // Volume geometry node
        SoVolumeTriangleStripSet pVolGeom = new SoVolumeTriangleStripSet();
        pVolGeom.numVertices.setValues(0, numVertices);
        pVolGeom.vertexProperty.setValue(myVertexProperty);

        // This ShapeHints will enable two-sided lighting for the geometry
        SoShapeHints pHints = new SoShapeHints();
        pHints.vertexOrdering.setValue(SoShapeHints.VertexOrderings.COUNTERCLOCKWISE);

        SbColor bkgColor = new SbColor(new SbColor(.7f, .7f, .7f));
        SoGradientBackground background = new SoGradientBackground();
        background.color0.setValue(bkgColor);
        background.color1.setValue(bkgColor);

        SoSeparator root = new SoSeparator();
        { // Assemble the scene graph
            // Note: Volume geometry must appear after the SoVolumeData node.
            root.addChild(background);
            root.addChild(makeBox(volSize)); // Draws volume bounds
            root.addChild(pVolData); // Volume data
            root.addChild(pTransFunc); // Transfer function
            root.addChild(pHints); // Shape hints
            root.addChild(new SoTransformerManip()); // Manip to move geometry
            root.addChild(pVolGeom); // Volume geometry
        }

        // Set up viewer:
        myViewer.setSceneGraph(root);
        myViewer.viewAll();

        final Component component = myViewer.getComponent();
        component.setPreferredSize(new java.awt.Dimension(600, 500));
        setLayout(new BorderLayout());
        add(component);
    }

    @Override
    public void stop()
    {
        myViewer.dispose();
    }

    ///////////////////////////////////////////////////////////////////////
    //
    // Create a wireframe box showing geometric bounds of volume
    private SoNode makeBox(SbBox3f volSize) {
        // Just draw the wireframe of the box
        SoDrawStyle pStyle = new SoDrawStyle();
        pStyle.style.setValue(SoDrawStyle.Styles.LINES);

        // The box will be easier to see without lighting
        SoLightModel pLModel = new SoLightModel();
        pLModel.model.setValue(SoLightModel.Models.BASE_COLOR);

        // The box should be unpickable so manip can be used inside it
        SoPickStyle pPickable = new SoPickStyle();
        pPickable.style.setValue(SoPickStyle.Styles.UNPICKABLE);

        // Translate cube (nominally at 0,0,0) to geometric center of volume
        SoTranslation pTranslate = new SoTranslation();
        pTranslate.translation.setValue(volSize.getCenter());

        // Create a cube with the geometric size of the volume
        SoCube pCube = new SoCube();
        float size[] = volSize.getSize().array;
        pCube.width.setValue(size[0]);
        pCube.height.setValue(size[1]);
        pCube.depth.setValue(size[2]);

        SoSeparator pBoxSep = new SoSeparator();
        { // Assemble scene graph
            pBoxSep.addChild(pStyle);
            pBoxSep.addChild(pLModel);
            pBoxSep.addChild(pPickable);
            pBoxSep.addChild(pTranslate);
            pBoxSep.addChild(pCube);
        }
        return pBoxSep;
    }
}
